#region Copyright
//       Copyright (c) 2009 - Antoine Jaussoin - http://www.jaussoin.com/blog/
// 
//        This program is free software: you can redistribute it and/or modify
//        it under the terms of the GNU General Public License as published by
//        the Free Software Foundation, either version 3 of the License, or
//        (at your option) any later version.
// 
//        This program is distributed in the hope that it will be useful,
//        but WITHOUT ANY WARRANTY; without even the implied warranty of
//        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//        GNU General Public License for more details.
// 
//        You should have received a copy of the GNU General Public License
//        along with this program.  If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Linq.Expressions;
using System.Reflection;
using FT.Architecture.Entities;
using FT.Architecture.Utils.Reflection;
using NHibernate;
using NHibernate.Metadata;
using NHibernate.Persister.Entity;
using NHibernate.Type;

namespace FT.Architecture.NHibernate.Repositories
{
    /// <summary>
    /// Provide information about the underlying storage
    /// </summary>
    /// <typeparam name="TId"></typeparam>
    /// <typeparam name="T"></typeparam>
    public class NHibernateUnderlyingStorage<TId, T> : IGenericUnderlyingStorage<TId, T> where T: class, IGenericEntity<TId>
    {
        private readonly NHibernateGenericRepository<T, TId> repository;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="repository"></param>
        public NHibernateUnderlyingStorage(NHibernateGenericRepository<T, TId> repository)
        {
            this.repository = repository;
        }

        /// <summary>
        /// Returns information about the storage, from an entity member
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        public StorageInfo? GetStorageInfo(Expression<Func<T, object>> member)
        {
            ISessionFactory factory = repository.SessionProvider.GetSession().SessionFactory;
            Type entityClass = typeof (T);
            PropertyInfo propertyInfo = Reflection.GetProperty(member);
            string propertyName = propertyInfo.Name;

            var metadata = (AbstractEntityPersister)factory.GetClassMetadata(entityClass);

            if (metadata != null)
            {
                try
                {
                    string[] columnNames = metadata.GetPropertyColumnNames(propertyName);
                    return new StorageInfo(metadata.TableName, columnNames);
                }
                catch (Exception)
                {
                    return null;
                }
                
            }

            return null;
        }

       
        /// <summary>
        /// Returns information about the entity from a table name, and a column name
        /// </summary>
        /// <param name="column"></param>
        /// <returns></returns>
        public EntityInfo<TId, T>? GetEntityInfo(string column)
        {
            ISessionFactory factory = repository.SessionProvider.GetSession().SessionFactory;
            Type entityClass = typeof(T);
            var metadata = (AbstractEntityPersister)factory.GetClassMetadata(entityClass);
            IClassMetadata metadata2 = factory.GetClassMetadata(entityClass);
            
            foreach (string propertyName in metadata2.PropertyNames)
            {
                string[] columnNames = metadata.GetPropertyColumnNames(propertyName);
                IType propertyType = metadata.GetPropertyType(propertyName);

                if (columnNames.Length == 1 
                    && columnNames[0] == column 
                    && !propertyType.IsCollectionType
                    && !propertyType.IsEntityType
                    && !propertyType.IsComponentType)
                {
                    MemberInfo[] members = entityClass.GetMember(propertyName);

                    if (members.Length == 1)
                    {
                        return new EntityInfo<TId, T>(entityClass, members[0]);
                    }
                }
            }

            string[] columnNames2 = metadata.GetPropertyColumnNames(metadata2.IdentifierPropertyName);
            if (columnNames2.Length == 1 && columnNames2[0] == column)
            {
                MemberInfo[] members = entityClass.GetMember(metadata2.IdentifierPropertyName);

                if (members.Length == 1)
                {
                    return new EntityInfo<TId, T>(entityClass, members[0]);
                }
            }

            return null;
        }

        
    }
}
